# Created by 黄景涛
# DATE: 2025/3/25

from selenium.common import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.webelement import WebElement

from base.kdt_lib.base_mixin import BaseMiXin

""" V4.x: 流程引擎相关的关键字 """


class WFAction(BaseMiXin):
    node_template = "//span[contains(text(),'{node_name}')]/ancestor::body[@xmlns]"
    # 完整的节点卡片
    node_loc = node_template + "/../.."
    # 节点卡片中的head区域
    node_head_loc = node_template + "//div[@class='action-node-head']"
    # 节点卡片中的content区域
    node_content_loc = node_template + "//div[@class='action-node-content']"
    # 分支线条中的head区域
    line_head_loc = node_template + "//div[@class='condition-node-head']"
    # 节点四个方向上的连接点
    direct_points = {
        "上": "x6-port-port-top",
        "下": "x6-port-port-bottom",
        "左": "x6-port-port-left",
        "右": "x6-port-port-right"
    }

    def key_displace_position(self, source_loc, target_loc):
        """
        源元素顶替目标元素的位置
        :param source_loc: 源元素定位
        :param target_loc: 目标元素定位
        :return:
        """
        # 获取元素对象
        source_ele: WebElement = self.driver.find_element(By.XPATH, source_loc)
        target_ele: WebElement = self.driver.find_element(By.XPATH, target_loc)

        # 计算目标距离量
        source_xy = tuple(source_ele.location.values())
        target_xy = tuple(target_ele.location.values())
        off_x, off_y = target_xy[0] - source_xy[0], target_xy[1] - source_xy[1]

        # 移动源元素至target坐标点
        action = ActionChains(self.driver)
        action.w3c_actions.pointer_action.click_and_hold(source_ele)

        # 模拟手动拖拽，逐渐增大location_xy的坐标值
        n = 5
        for i in range(n):
            x_ = off_x * (i + 1) // n
            y_ = off_y * (i + 1) // n
            action.w3c_actions.pointer_action.move_to_location(source_xy[0] + x_, source_xy[1] + y_)
        action.w3c_actions.pointer_action.release()
        action.perform()

    def key_drag_to_target_v4(self, source_loc, target_loc, direction: str, offset: str = "0"):
        """
        拖拽源元素至目标元素，支持上下左右方向 + 一定的偏移量
        :param source_loc: 源元素定位
        :param target_loc: 目标元素定位
        :param direction: 方向
        :param offset: 当前方向的偏移量
        :return:
        """
        direct_options = ('上', '下', '左', '右')

        # 参数校验
        if direction not in direct_options:
            raise ValueError(f"方向传值错误，参考：{direct_options}")

        try:
            offset = int(offset)
        except TypeError:
            raise TypeError(f"偏移量{offset}数据类型错误，请传入数字")

        # 元素对象
        source_ele: WebElement = self.driver.find_element(By.XPATH, source_loc)
        target_ele: WebElement = self.driver.find_element(By.XPATH, target_loc)

        # 获取元素的宽、高
        source_width, source_height = source_ele.rect['width'], source_ele.rect['height']
        target_width, target_height = target_ele.rect['width'], target_ele.rect['height']

        # 余量, 起微调作用
        margin_size = 0
        offset += margin_size
        up = (0, -(target_height // 2 + offset))
        down = (0, (target_height // 2 + offset))
        left = (-(target_width // 2 + offset), 0)
        right = ((target_width // 2 + offset), 0)
        sides_info = {"上": up, "下": down, "左": left, "右": right}

        action = ActionChains(self.driver)
        action.click_and_hold(source_ele).move_to_element_with_offset(target_ele,
                                                                      *sides_info[direction]).release().perform()

    def key_line_connect_v4(self, source_, target_):
        """
        :param self:
        :param source_: 源节点名称和连接点，  e.g: 提单,右   --连接点分四个方向：上下左右
        :param target_: 目标节点名称和连接点，  e.g: 新增节点,左
        :return:
        """

        def node_point_ele(node_name: str, point_key: str):
            """
            获取节点的连接点对象
            :param node_name: 节点名称
            :param point_key: 连接方向， 上下左右
            :return:
            """
            point_val = self.direct_points.get(point_key, None)
            if point_val is None:
                raise ValueError(f"节点的方向值错误，请参考：{','.join(self.direct_points.keys())}")
            point_loc = self.node_loc.format(node_name=node_name) + \
                        f"/*[local-name() = 'g' and contains(@class, '{point_val}')]"
            point_ele = self.driver.find_element(By.XPATH, point_loc)
            return point_ele

        # excel中的参数：e.g, 提单,右
        s_node_name, s_node_point = source_.strip().replace('，', ',').split(',')
        t_node_name, t_node_point = target_.strip().replace('，', ',').split(',')

        # 源节点对象
        s_node_loc = self.node_loc.format(node_name=s_node_name)
        s_node_ele = self.driver.find_element(By.XPATH, s_node_loc)
        actions = ActionChains(self.driver)
        actions.move_to_element(s_node_ele).perform()

        # 源连接点对象
        s_point_ele = node_point_ele(s_node_name, s_node_point)
        # 目标连接点对象
        t_point_ele = node_point_ele(t_node_name, t_node_point)

        # 两个连接点之间连线
        actions.click_and_hold(s_point_ele).move_to_element(t_point_ele).release()
        actions.perform()

    def key_edite_node_name(self, node_name, content=None):
        """
        :param self:
        :param node_name: 节点名称
        :param content: 输入内容
        :return:
        """
        try:
            # 点击前的title对象
            node_title_loc = self.node_head_loc.format(node_name=node_name) + "/div/div"
            pre_title_ele = self.driver.find_element(By.XPATH, node_title_loc)
            pre_title_ele.click()
            # 点击后，重新获取title对象
            edited_title_loc = self.node_head_loc.replace("[contains(text(),'{node_name}')]", "") + "//input"
            edited_title_ele = self.driver.find_element(By.XPATH, edited_title_loc)
        # 兼容分支线条
        except NoSuchElementException:
            node_title_loc = self.line_head_loc.format(node_name=node_name) + "/span[1]"
            pre_title_ele = self.driver.find_element(By.XPATH, node_title_loc)
            pre_title_ele.click()
            # 点击后，重新获取title对象
            edited_title_loc = self.line_head_loc.replace("[contains(text(),'{node_name}')]", "") + "//input"
            edited_title_ele = self.driver.find_element(By.XPATH, edited_title_loc)

        if content is not None:
            edited_title_ele.send_keys(content + Keys.ENTER)

    def key_click_node_config(self, node_name):
        """
        点击进入节点配置抽屉
        :param node_name: 节点名称
        :return:
        """
        # 获取长方形节点卡片的content区域
        try:
            node_content_loc = self.node_content_loc.format(node_name=node_name)
            node_content_ele = self.driver.find_element(By.XPATH, node_content_loc)
        except:
            # 兼容非长方形节点
            node_loc = self.node_loc.format(node_name=node_name)
            node_content_ele = self.driver.find_element(By.XPATH, node_loc)

        node_content_ele.click()

    def key_click_line_config(self, line_name):
        """
        点击进入分支线条配置抽屉
        :param line_name: 分支线条的名称
        :return:
        """
        line_prot_loc = self.line_head_loc.format(node_name=line_name) + "/span[2]"
        line_prot_ele = self.driver.find_element(By.XPATH, line_prot_loc)
        line_prot_ele.click()

